package cn.basic.social.manager.config;

import cn.basic.social.api.BaseResponse;
import cn.basic.social.api.exception.ApiException;
import cn.basic.social.api.utils.StringUtils;
import cn.basic.social.manager.handler.login.LoginRequest;
import cn.basic.social.manager.handler.login.LoginResponse;
import cn.basic.social.manager.handler.login.LoginService;
import cn.basic.social.manager.handler.qwtalkhandler.thirdparty.ThirdpartGetuserinfo3rdServiceImpl;
import cn.basic.social.manager.handler.wxtalkhandler.impl.WxGetAccessTokenServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

/**
 * @ClassName SocialRequestFactory
 * @Description 消息请求工厂
 * @Author zlf
 * @Date 2023/9/1 16:47
 */
@Slf4j
@RequiredArgsConstructor
public class SocialRequestFactory {

    private final SocialConfigProperties properties;
    private final RedisCache redisCache;

    /**
     * 指定对应的第三方服务类型SocialConfigProperties.TypeEnum的value，指定对应的应用名，一个第三方应用可以有好多个小程序应用、网站应用等。
     *
     * @param source          第三方服务类型SocialConfigProperties.TypeEnum的value
     * @param applicationName 对应的应用名
     * @param callback        具体实现
     * @return
     * @author zlf
     * @date 14:16 2023/9/28
     */
    public <T extends BaseResponse> T doExecute(String source, String applicationName, Callback callback) throws Exception {
        SocialConfigProperties.TypeEnum typeEnum = SocialConfigProperties.TypeEnum.getTypeEnumByName(source);
        if (typeEnum == null) {
            throw new ApiException("参数错误，未找到对应的source属性");
        }
        return this.doExecute(typeEnum, applicationName, callback);
    }

    /**
     * 指定对应的第三方服务类型SocialConfigProperties.TypeEnum，指定对应的应用名，一个第三方应用可以有好多个小程序应用、网站应用等。
     *
     * @param source          第三方服务类型SocialConfigProperties.TypeEnum
     * @param applicationName 对应的应用名
     * @param callback        具体实现
     * @return
     * @author zlf
     * @date 14:16 2023/9/28
     */
    public <T extends BaseResponse> T doExecute(SocialConfigProperties.TypeEnum source, String applicationName, Callback callback) throws Exception {
        Optional<SocialConfigProperties.TypeEnum> any = properties.getType().keySet().stream().filter(c -> c.name().equals(source.name())).findAny();
        if (!any.isPresent()) {
            throw new ApiException("未配置相应的属性值");
        }
        ProviderConfig providerConfig = properties.getType().get(source);
        // 指定对应的应用
        if (StringUtils.isNotEmpty(applicationName)) {
            Map<String, ProviderApplicationConfig> applicationMap = providerConfig.getApplication();
            if (applicationMap.containsKey(applicationName)) {
                ProviderApplicationConfig providerApplicationConfig = applicationMap.get(applicationName);
                Map<String, ProviderApplicationConfig> sourceMap = new HashMap<>();
                sourceMap.put(applicationName, providerApplicationConfig);
                providerConfig.setApplication(sourceMap);
            } else {
                throw new ApiException("未查询到的应用key配置");
            }
        }

        return (T) callback.execute(providerConfig, redisCache);
    }

    /**
     * @ClassName Callback
     * @Description 定义一个函数式接口callback，用于调用具体的接口
     * @Author zlf
     * @Date 2023/9/1 16:47
     */
    @FunctionalInterface
    public interface Callback<T extends BaseResponse> {
        /**
         * 指定具体的接口
         *
         * @param providerConfig 配置类
         * @param redisCache     redid实例
         * @return
         * @author zlf
         * @date 14:21 2023/9/28
         */
        T execute(ProviderConfig providerConfig, RedisCache redisCache) throws Exception;
    }

    /**
     * 登录获取用户信息
     *
     * @param
     * @return
     * @author zlf
     * @date 16:02 2023/9/8
     */
    public LoginResponse doLoginExecute(String source, LoginRequest loginRequest) throws Exception {
        SocialConfigProperties.TypeEnum typeEnum = SocialConfigProperties.TypeEnum.getTypeEnumByName(source);
        if (typeEnum == null) {
            throw new ApiException("参数错误，未找到对应的source属性");
        }
        Optional<SocialConfigProperties.TypeEnum> any = properties.getType().keySet().stream().filter(c -> c.name().equals(typeEnum.name())).findAny();
        if (!any.isPresent()) {
            throw new ApiException("未配置相应的属性值");
        }
        // 校验state
        if (StringUtils.isBlank(loginRequest.getState()) || !redisCache.containsKey(loginRequest.getState())) {
            throw new ApiException("无效的state");
        }
        redisCache.get(loginRequest.getState());
        LoginService loginService = null;
        switch (typeEnum) {
            case QiWei_ThirdPart:
                loginService = new ThirdpartGetuserinfo3rdServiceImpl();
                break;
            case WeiXin:
                loginService = new WxGetAccessTokenServiceImpl();
                break;
            default:
                // 默认是企业微信服务商登录
                loginService = new ThirdpartGetuserinfo3rdServiceImpl();
                break;
        }
        ProviderConfig providerConfig = properties.getType().get(typeEnum);
        return loginService.invokeLogin(loginRequest, providerConfig, redisCache);
    }

    /**
     * 登录获取state，用于授权后的校验
     *
     * @param
     * @return state 用于等于鉴权
     * @author zlf
     * @date 16:02 2023/9/8
     */
    public String getLoginState() throws Exception {
        String state = UUID.randomUUID().toString().replace("-", "");
        // state只保存30分钟， 如果扫码30分钟后还没授权，他将登录失败
        redisCache.cache(state, "on", 30 * 60 * 10000);
        return state;
    }


}
